package com.cwnu.sw.cys.core.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.cwnu.sw.cys.core.common.constant.MQConstant;
import com.cwnu.sw.cys.core.common.constant.RedisKey;
import com.cwnu.sw.cys.common.transaction.service.MQProducer;
import com.cwnu.sw.cys.core.user.dao.UserDao;
import com.cwnu.sw.cys.core.user.domain.dto.LoginMessageDTO;
import com.cwnu.sw.cys.core.user.domain.dto.ScanSuccessMessageDTO;
import com.cwnu.sw.cys.core.user.domain.entity.User;
import com.cwnu.sw.cys.core.user.service.UserService;
import com.cwnu.sw.cys.core.user.service.WxMsgService;
import com.cwnu.sw.cys.core.user.service.adapter.TextBuilder;
import com.cwnu.sw.cys.core.user.service.adapter.UserAdapter;
import com.cwnu.sw.cys.core.common.utils.RedisUtils;
import com.cwnu.sw.cys.core.websocket.service.WebSocketService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.net.URLEncoder;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author jisam
 * @version 1.0
 * @contact "jisamchan@outlook.com"
 * @date 13/02/2025
 * @description
 */
@Service
@Slf4j
public class WxMsgServiceImpl implements WxMsgService {

    // 从Spring配置文件中注入微信公众号回调地址
    // 该地址用于微信用户授权后重定向到的页面
    // 使用Spring的@Value注解自动注入配置项，提高配置的灵活性和可维护性
    @Value("${wx.mp.callback}")
    private String callback;

    @Autowired
    private UserDao userDao;

    @Autowired
    private UserService userService;

    @Lazy
    @Autowired
    private WxMpService wxMpService;

    @Lazy
    @Autowired
    private WebSocketService webSocketService;


    @Autowired
    private MQProducer mqProducer;
    @Override
    public WxMpXmlOutMessage scan(WxMpXmlMessage wxMpXmlMessage) {
        String openId = wxMpXmlMessage.getFromUser();
        Integer code = getEventKey(wxMpXmlMessage);
        if(Objects.isNull(code)){
            return null;
        }
        User user = userDao.getUserByOpenId(openId);
        log.info("scan openId:{},code:{}",openId,code);
        boolean registered = Objects.nonNull(user);
        boolean authorized = false;
        try {
            authorized = StringUtils.isNotEmpty(user.getAvatar());
        }catch (Exception e){
            log.info("user authorized fail");
        }
        // 注册并授权
        if(registered && authorized){
            // todo 登录成功逻辑
            // 通过code给channel推送消息
            mqProducer.sendMsg(MQConstant.LOGIN_MSG_TOPIC,new LoginMessageDTO(user.getId(),code));
//            return new TextBuilder().build(String.format("欢迎登录，%s", user.getName()), wxMpXmlMessage, wxMpService);
            //webSocketService.scanLoginSuccess(code,user.getId());
            return TextBuilder.build(String.format("扣你鸡娃,%s", user.getName()), wxMpXmlMessage, wxMpService);
        }

        registered = Objects.nonNull(user);
        if(!registered)
        {
            // 未注册逻辑
            User insertUser =  UserAdapter.builderUserSave(openId);
            Long id = userService.register(insertUser);
            log.info("新注册id:"+id);
        }

        // 在redis中保存openid和场景code的关系，后续才能通知到前端,旧版数据没有清除,这里设置了过期时间
        RedisUtils.set(RedisKey.getKey(RedisKey.OPEN_ID_STRING,openId),code,300, TimeUnit.SECONDS);
        // 授权流程，给用户发送授权信息，并异步通知到前端扫码，等授权
        mqProducer.sendMsg(MQConstant.SCAN_MSG_TOPIC,new ScanSuccessMessageDTO(code));
        // webSocketService.scanSuccess(code);
        //done 扫码事件处理

        // 构造授权URL
        // 微信OAuth2认证的URL模板，用于构建微信登录的授权请求URL
        // 包含了微信OAuth2认证所需的基本参数，如appid、redirect_uri等
        // 该URL模板是根据微信开放平台的OAuth2认证文档定义的
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
        String authUrl = String.format(url, wxMpService.getWxMpConfigStorage().getAppId(), URLEncoder.encode(callback + "/wx/portal/public/callBack"));

//        WxMpXmlOutMessage.TEXT().build();
        // 构建回复消息，引导用户登录
        return TextBuilder.build("扣你鸡娃,请点击授权" + "<a href=\""+authUrl+"\">→登录</a>", wxMpXmlMessage,wxMpService);
    }

    @Override
    public void authorize(WxOAuth2UserInfo userInfo) {
        User user = userDao.getUserByOpenId(userInfo.getOpenid());
        if(StringUtils.isBlank(user.getName())){
            fillUserInfo(user.getId(),userInfo);
        }
        Integer code = RedisUtils.get(RedisKey.getKey(RedisKey.OPEN_ID_STRING,userInfo.getOpenid()),Integer.class);
        mqProducer.sendMsg(MQConstant.LOGIN_MSG_TOPIC,new LoginMessageDTO(user.getId(),code));
        //webSocketService.scanLoginSuccess(code,user.getId());
    }

    private void fillUserInfo(Long uid, WxOAuth2UserInfo userInfo) {
        User updateUser = UserAdapter.buildAuthUser(uid,userInfo);
        int testTime = 5;
        for(int i = 0; i < testTime; i++){
            try{
                    userDao.updateById(updateUser);
                    return;
                }catch (DuplicateKeyException e) {
                    log.info("fill userInfo duplicate uid:{},info:{}", uid, userInfo);
                } catch (Exception e) {
                    log.info("fill userInfo fail uid:{},info:{}", uid, userInfo);
                }
                updateUser.setName("名字重置" + RandomUtil.randomInt(100000));
            }
        }

    private Integer getEventKey(WxMpXmlMessage wxMpXmlMessage) {
        //done qrscene_2 扫码关注事件处理 统一成数字
        try {
            String rawEventKey = wxMpXmlMessage.getEventKey();
            String eventKey = rawEventKey.replace("qrscene_", "");
            return Integer.parseInt(eventKey);
        } catch (Exception e) {
            log.info("eventKey is not number");
            return null;
        }
    }
}
